﻿using Microsoft.Xna.Framework.Media;
using NiklasKarl.WarpCam.Effects;
using Nokia.Graphics.Imaging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Windows.Phone.Media.Capture;

namespace NiklasKarl.WarpCam.Models
{
    public class AppModel
    {
        private EventWaitHandle m_sync;

        private CameraSensorLocation m_location;
        private PhotoCaptureDevice m_device;
        private CameraPreviewImageSource m_source;

        private Windows.Foundation.Size m_captureResolution;
        private Windows.Foundation.Size m_previewResolution;
        private Rotation m_rotation;
        private FlipMode m_flip;

        private List<ICameraEffect> m_effects;


        public EventWaitHandle CameraSync { get { return m_sync; } }

        public CameraSensorLocation CameraLocation { get { return m_location; } }
        public PhotoCaptureDevice CameraDevice { get { return m_device; } }
        public IImageProvider CameraSource { get { return m_source; } }

        public static bool LocationSwitchPossible { get { return PhotoCaptureDevice.AvailableSensorLocations.Count > 1; } }
        public Windows.Foundation.Size CameraCaptureResolution { get { return m_captureResolution; } }
        public Windows.Foundation.Size CameraPreviewResolution { get { return m_previewResolution; } }
        public Rotation CameraRotation { get { return m_rotation; } }
        public FlipMode CameraFlip { get { return m_flip; } }

        public IReadOnlyList<ICameraEffect> Effects { get { return m_effects; } }


        public AppModel(CameraSensorLocation desiredLocation)
        {
            m_sync = new EventWaitHandle(false, EventResetMode.ManualReset);

            m_location = desiredLocation;
            if (!PhotoCaptureDevice.AvailableSensorLocations.Contains(m_location))
            {
                m_location = (CameraSensorLocation)((((int)m_location) + 1) % 2);;
            }
        }

        public Task InitializeAsync()
        {
            Task task = InitializeVolatileResourcesAsync();

            m_effects = new List<ICameraEffect>(
                new ICameraEffect[]
                {
                    new WarpHandler(WarpEffect.SmallNose, 8),
                    new WarpHandler(WarpEffect.Upnose, 8),
                    new WarpHandler(WarpEffect.BigNose, 8),
                    new WarpHandler(WarpEffect.Alien, 8),
                    null,
                    new WarpHandler(WarpEffect.WideSmile, 8),
                    new WarpHandler(WarpEffect.Square, 8),
                    new WarpHandler(WarpEffect.Twister, 8),
                    new WarpHandler(WarpEffect.Grit, 8)
                });

            return task;
        }

        public async Task InitializeVolatileResourcesAsync()
        {
            ReleaseVolatileResources();

            IReadOnlyList<Windows.Foundation.Size> captureResolutions = PhotoCaptureDevice.GetAvailableCaptureResolutions(m_location);
            IReadOnlyList<Windows.Foundation.Size> previewResolutions = PhotoCaptureDevice.GetAvailablePreviewResolutions(m_location);

            foreach (Windows.Foundation.Size resolution in captureResolutions)
            {
                if (Math.Min(resolution.Width, resolution.Height) / Math.Max(resolution.Width, resolution.Height) == 0.75)
                {
                    m_captureResolution = resolution;
                    break;
                }
            }

            foreach (Windows.Foundation.Size resolution in previewResolutions)
            {
                if (Math.Min(resolution.Width, resolution.Height) / Math.Max(resolution.Width, resolution.Height) == 0.75)
                {
                    m_previewResolution = resolution;
                }
            }

            m_device = await PhotoCaptureDevice.OpenAsync(m_location, m_captureResolution);
            await m_device.SetPreviewResolutionAsync(m_previewResolution);

            if (m_location == CameraSensorLocation.Front)
            {
                m_rotation = (Rotation)(uint)((360.0f - (float)m_device.SensorRotationInDegrees) / 90f + 0.5f);
                m_flip = FlipMode.None;
            }
            else
            {
                m_rotation = (Rotation)(uint)(((float)m_device.SensorRotationInDegrees) / 90f + 0.5f);
                m_flip = FlipMode.None;
            }

            m_source = new CameraPreviewImageSource(m_device);

            m_sync.Set();
        }

        public void ReleaseVolatileResources()
        {
            m_sync.Reset();

            m_captureResolution = Windows.Foundation.Size.Empty;
            m_previewResolution = Windows.Foundation.Size.Empty;

            if (m_source != null)
            {
                m_source = null;
            }

            if (m_device != null)
            {
                m_device.Dispose();
                m_device = null;
            }
        }


        public async Task SwitchLocationAsync()
        {
            CameraSensorLocation cameraLocation = (CameraSensorLocation)((((int)m_location) + 1) % 2);

            if (PhotoCaptureDevice.AvailableSensorLocations.Contains(cameraLocation))
            {
                m_location = cameraLocation;

                await InitializeVolatileResourcesAsync();
            }
        }
    }
}
